home *** CD-ROM | disk | FTP | other *** search
- /* TCP/IP based server for HyperText HTDaemon.c
- ** ---------------------------------
- **
- **
- ** Compilation options:
- ** RULES If defined, use rule file and translation table
- ** DIR_OPTIONS If defined, -d options control directory access
- **
- ** Authors:
- ** TBL Tim Berners-Lee, CERN
- ** JFG Jean-Francois Groff, CERN
- ** JS Jonathan Streets, FNAL
- **
- ** History:
- ** Sep 91 TBL Made from earlier daemon files. (TBL)
- ** 26 Feb 92 JFG Bug fixes for Multinet.
- ** 8 Jun 92 TBL Bug fix: Perform multiple reads in case we don't get
- ** the whole command first read.
- ** 25 Jun 92 JFG Added DECNET option through TCP socket emulation.
- ** 6 Jan 93 TBL Plusses turned to spaces between keywords
- ** 7 Jan 93 JS Bug fix: addrlen had not been set for accept() call
- ** Logging in GMT to file-YYMM in name
- */
- /* (c) CERN WorldWideWeb project 1990-1992. See Copyright.html for details */
-
-
- /* Module parameters:
- ** -----------------
- **
- ** These may be undefined and redefined by syspec.h
- */
-
- #define LISTEN_BACKLOG 2 /* Number of pending connect requests (TCP)*/
- #define MAX_CHANNELS 20 /* Number of channels we will keep open */
- #define WILDCARD '*' /* Wildcard used in addressing */
- #define FIRST_TCP_PORT 5000 /* When using dynamic allocation */
- #define LAST_TCP_PORT 5999
-
- #define MAX_LINE 512 /* HTTP request field line */
-
- #ifndef RULE_FILE
- #define RULE_FILE "/etc/httpd.conf"
- #endif
-
- #ifndef DEFAULT_EXPORT
- #define DEFAULT_EXPORT "/Public"
- #endif
-
- #include "HTUtils.h"
- #include "tcp.h" /* The whole mess of include files */
- #include "HTTCP.h" /* Some utilities for TCP */
- #include "HTFormat.h"
- #include "HTInit.h"
-
- #ifdef RULES /* Use rules? */
- #include "HTRules.h"
- #endif
-
- #include "HTFile.h"
- #include "HTParse.h"
-
- extern int HTRetrieve PARAMS((char * arg, int soc)); /* Handle one request */
-
-
-
- /* Module-Global Variables
- ** -----------------------
- */
- PRIVATE enum role_enum {master, slave, transient, passive} role;
- PRIVATE SockA soc_address;
- PRIVATE int soc_addrlen;
- PRIVATE int master_soc; /* inet socket number to listen on */
- PRIVATE int com_soc; /* inet socket number to read on */
- #ifdef SELECT
- PRIVATE fd_set open_sockets; /* Mask of channels which are active */
- PRIVATE int num_sockets; /* Number of sockets to scan */
- #endif
- PRIVATE BOOLEAN dynamic_allocation; /* Search for port? */
-
- PRIVATE time_t theTime; /* A long integer giving the datetime in secs */
- PRIVATE struct tm * gmt; /* The time in GMT broken down */
-
- /* Program-Global Variables
- ** ------------------------
- */
- PUBLIC char * HTAppName = "CERN-HTTPD"; /* Application name */
- PUBLIC char * HTAppVersion = VD; /* Application version */
-
-
- /* PUBLIC int WWW_TraceFlag; in libwww/HTString.c Control flag for diagnostics */
-
- PUBLIC char * log_file_name = 0;/* Log file name if any (WAIS code) */
-
- PUBLIC char * HTClientProtocol = 0; /* Protocol and version number */
- PUBLIC char * HTServerProtocol = "HTTP/1.0";
-
- extern char * HTClientHost; /* in library or HTRetrieve */
-
- PRIVATE FILE * serverlog; /* Log file if any -- don't set up public one */
-
- #define SPACE(c) ((c==' ')||(c=='\t')||(c=='\n')||(c=='\r')) /* DMX */
-
-
-
- /* Split fields
- ** ------------
- **
- ** On entry,
- ** s points to string with words or quoted strings as fields
- ** separated by white space
- ** On exit,
- ** Return value points to first char of second word or NULL if none.
- ** First word is null-terminated.
- ** All trailing white space is overwritten with zero.
- */
- PRIVATE char * next_field ARGS1(char *, s)
- {
- while(*s && SPACE(*s))s++; /* skip leading blanks */
- switch(*s) {
- case '"':
- case '\'':
- s = strchr(s, *s); /* skip quoted word */
- if (!s) return s; /* No closing quote! */
- s++; /* Skip closing quote */
- break;
- default:
- while(*s && !SPACE(*s)) s++; /* skip word */
- }
- if (!*s) return (char*)0; /* Only one or no word */
-
- *s++ = (char)0; /* terminate first word */
- while(SPACE(*s))s++; /* skip leading blanks */
- if (!*s) return (char*)0; /* No second word */
- return s;
- }
-
-
- /* Send a string down a socket
- ** ---------------------------
- **
- ** The trailing zero is not sent.
- ** There is a maximum string length.
- */
- PUBLIC int HTWriteASCII ARGS2(int, soc, char *, s)
- {
- #ifdef NOT_ASCII
- char * p;
- char ascii[255];
- char *q = ascii;
- for (p=s; *p; p++) {
- *q++ = TOASCII(*p);
- }
- return NETWRITE(soc, ascii, p-s);
- #else
- return NETWRITE(soc, s, strlen(s));
- #endif
- }
-
-
-
- /* MIME OUTPUT
- ** ===========
- */
-
- struct _HTStream {
- HTStreamClass * isa;
- HTStreamClass class; /* special per object */
- };
-
- /* Write header for given content type
- ** -----------------------------------
- **
- **
- ** Note: Under old protocol, non-plaintext files are sent untouched
- ** to work with multimedia hack in XMosaic.
- */
- #ifdef NOT_NEEDED_IT_SEEMS
- PUBLIC void HTSendHeader ARGS2(
- int, soc,
- HTFormat, rep)
- {
- if (TRACE) fprintf(stderr, "HTDaemon: Retrieve Ok, Content-type %s\n",
- HTAtom_name(rep));
- if (HTClientProtocol) {
- char buffer[4096]; /* @@ */
- sprintf(buffer, "%s%s%s%s%s",
- HTServerProtocol,
- " 200 Document follows\r\nMIME-Version: 1.0\r\n",
- "Content-Type: ", HTAtom_name(rep), "\r\n\r\n");
- HTWriteASCII(soc, buffer);
-
- } else { /* Old protocol */
- if (rep == WWW_PLAINTEXT) {
- HTWriteASCII(soc, "<PLAINTEXT>\r\n");
- }
- }
- }
- #endif
-
- /* Catch error messages
- ** --------------------
- **
- ** This shouldn't be necessary most of the time as HTLoadError
- ** will be called.
- **
- ** These entry points suppress the loading of HTAlert from the WWW library.
- ** These could be cleaned up and made very useful, esp
- ** remote progress reporting...
- */
-
- PUBLIC void HTAlert ARGS1(CONST char *, Msg)
- {
- fprintf(stderr, "500 Server reports: %s\r\n", Msg);
- }
-
-
- PUBLIC void HTProgress ARGS1(CONST char *, Msg)
- {
- /* fprintf(stderr, " %s ...\n", Msg); */
- }
-
-
- PUBLIC BOOL HTConfirm ARGS1(CONST char *, Msg)
- {
- return(NO);
- }
-
- /* Prompt for answer and get text back
- */
- PUBLIC char * HTPrompt ARGS2(CONST char *, Msg, CONST char *, deflt)
- {
- char * rep = 0;
- StrAllocCopy(rep, deflt);
- return rep;
- }
-
- /* Write Error Message
- ** -------------------
- **
- **
- */
- PUBLIC int HTLoadError ARGS3(
- HTStream*, sink,
- int, number,
- CONST char *, message)
- {
- char buffer[4096]; /* @@ */
- if (TRACE) fprintf(stderr, "HTDaemon: *** Returning ERROR %d:\n %s\n",
- number, message);
- if (HTClientProtocol) {
- sprintf(buffer,
- "%s %d %s\r\nMIME-Version: 1.0\r\nContent-Type: text/HTML\r\n\r\n",
- HTServerProtocol,
- number,
- message);
- (*sink->isa->put_string)(sink, buffer);
- }
- sprintf(buffer,
- "<BODY><H1>Error %d</H1>\r\n\r\n %s</BODY>\r\n",
- number,
- message);
- (*sink->isa->put_string)(sink, buffer);
- (*sink->isa->end_document)(sink);
- (*sink->isa->free)(sink);
- return HT_LOADED;
- }
-
-
- /* Converter for writing the MIME wrapper to a document
- ** ----------------------------------------------------
- **
- ** Thsi kinda cheats because it returns the sink stream
- ** having first sent the wrapper down it. Efficient -- I like it
- ** better than the MIME parser which gets in the way from then on.
- */
- PUBLIC HTStream * HTMIMEWrapper ARGS3(
- HTPresentation *, pres,
- HTParentAnchor *, anchcor,
- HTStream*, sink)
- {
-
- if (TRACE) fprintf(stderr, "HTDaemon: Retrieve Ok, Content-type %s\n",
- HTAtom_name(pres->rep));
- if (HTClientProtocol) {
- char buffer[4096]; /* @@ */
- sprintf(buffer, "%s%s%s%s%s",
- HTServerProtocol,
- " 200 Document follows\r\nMIME-Version: 1.0\r\n",
- "Content-Type: ", HTAtom_name(pres->rep), "\r\n\r\n");
- (*sink->isa->put_string)(sink, buffer);
-
- } else { /* Old protocol */
- if (pres->rep == WWW_PLAINTEXT) {
- (*sink->isa->put_string)(sink, "<PLAINTEXT>\r\n");
- }
- }
-
- return sink;
- }
-
-
- /*____________________________________________________________________
- **
- ** Networking code
- */
-
- /* Bind to a TCP port
- ** ------------------
- **
- ** On entry,
- ** tsap is a string explaining where to take data from.
- ** "" means data is taken from stdin.
- ** "*:1729" means "listen to anyone on port 1729"
- **
- ** On exit,
- ** returns Negative value if error.
- */
- int do_bind ARGS1(CONST char *, tsap)
- {
- #ifdef SELECT
- FD_ZERO(&open_sockets); /* Clear our record of open sockets */
- num_sockets = 0;
- #endif
-
- /* Deal with PASSIVE socket:
- **
- ** A passive TSAP is one which has been created by the inet daemon.
- ** It is indicated by a void TSAP name. In this case, the inet
- ** daemon has started this process and given it, as stdin, the connection
- ** which it is to use.
- */
- if (*tsap == 0) { /* void tsap => passive */
-
- dynamic_allocation = FALSE; /* not dynamically allocated */
- role = passive; /* Passive: started by daemon */
-
- #ifdef vms
-
- { unsigned short channel; /* VMS I/O channel */
- struct string_descriptor { /* This is NOT a proper descriptor*/
- int size; /* but it will work. */
- char *ptr; /* Should be word,byte,byte,long */
- } sys_input = {10, "SYS$INPUT:"};
- int status; /* Returned status of assign */
- extern int sys$assign();
-
- status = sys$assign(&sys_input, &channel, 0, 0);
- com_soc = channel; /* The channel is stdin */
- CTRACE(tfp, "IP: Opened PASSIVE socket %d\n", channel);
- return 1 - (status&1);
- }
- #else
- com_soc = 0; /* The channel is stdin */
- CTRACE(tfp, "IP: PASSIVE socket 0 assumed from inet daemon\n");
- return 0; /* Good */
- #endif
-
- /* Parse the name (if not PASSIVE)
- */
- } else { /* Non-void TSAP */
- char *p; /* pointer to string */
- char *q;
- struct hostent *phost; /* Pointer to host - See netdb.h */
- char buffer[256]; /* One we can play with */
- register SockA * sin = &soc_address;
-
- strcpy(buffer, tsap);
- p = buffer;
-
- /* Set up defaults:
- */
- #ifdef DECNET
- sin->sdn_family = AF_DECnet; /* Family = DECnet, host order */
- sin->sdn_objnum = 0; /* Default: new object number, */
- #else /* Internet */
- sin->sin_family = AF_INET; /* Family = internet, host order */
- sin->sin_port = 0; /* Default: new port, */
- #endif
- dynamic_allocation = TRUE; /* dynamically allocated */
- role = passive; /* by default */
-
- /* Check for special characters:
- */
- if (*p == WILDCARD) { /* Any node */
- role = master;
- p++;
- }
-
- /* Strip off trailing port number if any:
- */
- for(q=p; *q; q++)
- if (*q==':') {
- int status = 0;
- *q++ = 0; /* Terminate node string */
- #ifdef DECNET
- sin->sdn_objnum = (unsigned char) HTCardinal(
- &status, &q, (unsigned int)65535);
- #else
- sin->sin_port = htons((unsigned short)HTCardinal(
- &status, &q, (unsigned int)65535));
- if (status<0) return status;
- #endif
- if (*q) return -2; /* Junk follows port number */
- dynamic_allocation = FALSE;
- break; /* Exit from loop before we skip the zero */
- } /*if*/
-
- /* Get node name:
- */
- #ifdef DECNET /* Empty address (don't care about the command) */
- sin->sdn_add.a_addr[0] = 0;
- sin->sdn_add.a_addr[1] = 0;
- CTRACE(tfp,
- "Daemon: Parsed address as port %d, DECnet %d.%d\n",
- (int) sin->sdn_objnum,
- (int) sin->sdn_add.a_addr[0],
- (int) sin->sdn_add.a_addr[1] ) ;
- #else
- if (*p == 0) {
- sin->sin_addr.s_addr = INADDR_ANY; /* Default: any address */
-
- } else if (*p>='0' && *p<='9') { /* Numeric node address: */
- sin->sin_addr.s_addr = inet_addr(p); /* See arpa/inet.h */
-
- } else { /* Alphanumeric node name: */
- phost=gethostbyname(p); /* See netdb.h */
- if (!phost) {
- CTRACE(tfp, "IP: Can't find internet node name `%s'.\n",p);
- return HTInetStatus("gethostbyname"); /* Fail? */
- }
- memcpy(&sin->sin_addr, phost->h_addr, phost->h_length);
- }
- CTRACE(tfp,
- "Daemon: Parsed address as port %d, inet %d.%d.%d.%d\n",
- (int)ntohs(sin->sin_port),
- (int)*((unsigned char *)(&sin->sin_addr)+0),
- (int)*((unsigned char *)(&sin->sin_addr)+1),
- (int)*((unsigned char *)(&sin->sin_addr)+2),
- (int)*((unsigned char *)(&sin->sin_addr)+3));
- #endif
- } /* scope of p */
-
-
- /* Master socket for server:
- */
- if (role == master) {
-
- /* Create internet socket
- */
- #ifdef DECNET
- master_soc = socket(AF_DECnet, SOCK_STREAM, 0);
- #else
- master_soc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- #endif
- if (master_soc<0)
- return HTInetStatus("socket");
-
- CTRACE(tfp, "IP: Opened socket number %d\n", master_soc);
-
- /* If the port number was not specified, then we search for a free one.
- */
- #ifndef DECNET /* irrelevant: no inetd */
- if (dynamic_allocation) {
- unsigned short try;
- for (try=FIRST_TCP_PORT; try<=LAST_TCP_PORT; try++) {
- soc_address.sin_port = htons(try);
- if (bind(master_soc,
- (struct sockaddr*)&soc_address,
- /* Cast to generic sockaddr */
- sizeof(soc_address)) == 0)
- break;
- if (try == LAST_TCP_PORT)
- return HTInetStatus("bind");
- }
- CTRACE(tfp, "IP: Bound to port %d.\n",
- ntohs(soc_address.sin_port));
- } else
- #endif
- { /* Port was specified */
- if (bind(master_soc,
- (struct sockaddr*)&soc_address, /* Cast to generic address */
- sizeof(soc_address))<0)
- return HTInetStatus("bind");
- }
- if (listen(master_soc, LISTEN_BACKLOG)<0)
- return HTInetStatus("listen");
-
- CTRACE(tfp, "Daemon: Master socket(), bind() and listen() all OK\n");
- #ifdef SELECT
- FD_SET(master_soc, &open_sockets);
- if ((master_soc+1) > num_sockets) num_sockets=master_soc+1;
- #endif
- return master_soc;
- } /* if master */
-
- return -1; /* unimplemented role */
-
- } /* do_bind */
-
-
- /* Read One Line
- ** -------------
- **
- ** On exit,
- ** returns A malloced buffer with the line in.
- ** Rest of buffer is intact in command
- ** not converted into ASCII in case it is binary.
- */
- #define COMMAND_SIZE 2048 /* @@@@ WAIS queries can be big! */
- #define MINIMUM_READ 256 /* Minimum left before we reallocate */
- #define ALLOCATION_UNIT 2048 /* Amount extra we add on each time */
-
- PRIVATE int allocated = 0;
- PRIVATE int write_pointer = 0;
- PRIVATE int read_pointer = 0;
- PRIVATE char * command = NULL;
-
- PRIVATE char * get_line ARGS1(
- int, soc)
-
- {
- char * line;
- int status;
- if (!command) { /* First allocation */
- allocated = COMMAND_SIZE;
- write_pointer = 0; /* valid data in buffer */
- read_pointer = 0;
- command = (char *)malloc(allocated+1);
- *command = 0; /* terminate "left-over" */
- if (!command) {
- fprintf(stderr, "Daemon: insufficient memory!\n");
- exit(-5);
- }
- }
-
- for(;;) { /* Get more if needed to complete line */
- if (read_pointer == write_pointer) { /* Need more data */
- if (allocated - write_pointer < MINIMUM_READ) {
- allocated = allocated + ALLOCATION_UNIT;
- command = (char *)realloc(command, allocated+1);
- if (!command) {
- fprintf(stderr, "Daemon: No memory to reallocate command buffer!!\n");
- exit(-6);
- }
- }
- status = NETREAD(soc, command + write_pointer, allocated - write_pointer);
- if (TRACE) fprintf(stderr,
- "Daemon: net read returned %d, errno=%d\n", status, errno);
- if (status<=0) {
- free(command);
- return NULL; /* EOF or error before NL */
- }
- write_pointer = write_pointer + status;
- command[write_pointer] = 0; /* terminate new string */
- }
-
- /* Find a line feed if there is one */
-
- for(; read_pointer < write_pointer; read_pointer++) {
- char c;
- #ifdef NOT_ASCII
- command[read_pointer] = TOASCII(command[read_pointer]);
- #endif
- c = command[read_pointer];
- if (!c) {
- free(command);
- return NULL; /* Panic! read a 0! */
- }
- if (c=='\n') { /* found a line feed: split buffers*/
- line = command;
- command[read_pointer++] = 0; /* terminate and split lines */
- write_pointer = write_pointer - read_pointer;
- command = (char *)malloc(allocated+1);
- if (!command) {
- fprintf(stderr, "Daemon: insufficient memory for line!\n");
- exit(-7);
- }
- memcpy(command, &line[read_pointer], write_pointer);
- read_pointer = 0;
- return line;
- }
-
- } /* scan over buffer */
- } /* end loop getting and scanning data */
- }
-
-
- /* Handle one message
- ** ------------------
- **
- ** On entry,
- ** soc A file descriptor for input and output.
- ** On exit,
- ** returns >0 Channel is still open.
- ** 0 End of file was found, please close file
- ** <0 Error found, please close file.
- */
- PUBLIC int HTHandle ARGS1(int, soc)
- {
-
- char *line1; /* To hold command read from client */
- char *keywords; /* pointer to keywords in address */
- int status;
- char * arg; /* Pointer to the argument string */
-
-
- if (command) {
- free(command);
- command = 0;
- }
-
- line1 = get_line(soc); /* Free me later */
-
- /* Log the call:
- */
- if (serverlog) {
- fprintf(serverlog, "%24.24s %s %s\n",
- asctime(gmt), HTClientHost, line1);
- fflush(serverlog); /* Actually update it on disk */
- if (TRACE) fprintf(stderr, "Log: %24.24s %s %s",
- asctime(gmt), HTClientHost, line1);
- }
-
- arg=next_field(line1);
-
- /* @@@@@@@@@ Clear out any conversions to "present" left from before !!
- ** for when running in a loop
- */
- HTFormatInit(); /* set up the list */
-
- #ifndef NOCONVERT
- HTSetConversion("text/plain", "www/present", HTMIMEWrapper,
- 1.0, 0.0, 0.0);
- HTSetConversion("text/html", "www/present", HTMIMEWrapper,
- 1.0, 0.0, 0.0);
- HTSetConversion("application/binary", "www/present", HTMIMEWrapper,
- 0.7, 0.0, 0.0); /* Risky & more trouble */
- #endif
- if (arg) {
-
- HTClientProtocol = next_field(arg);
-
- if (HTClientProtocol) {
-
- char *line; /* free me! @@@@@ */
- char *q;
- enum _request_field { INVALID, ACCEPT } field = INVALID;
-
- (void) next_field(HTClientProtocol); /* Strip trailing space */
- while((line=get_line(soc)) != 0) {
- char * p;
- if (!*line) break; /* Just LF */
- p = line + strlen(line) - 1;
- if (*p == '\r') *p = 0;
- if (!*line) break; /* Just CRLF */
- if (!WHITE(*line)) { /* has field */
- p = strchr(line, ':');
- if (!p) break; /* Bad format -- junk line */
- field = !strncasecmp(line, "Accept:", 7) ? ACCEPT
- : INVALID;
- p++; /* skip colon */
- } else { /* continuation line */
- p = line;
- }
- if (field != INVALID) { /* Look for good one */
- float quality = 1.0;
- float maxbytes = 0.0;
- float maxsecs = 0.0;
-
- q = next_field(p);
- p = HTStrip(p); /* Strip leading and trailing */
-
- while (q) { /* more data left */
- float value;
- char * next;
- char *equals = strchr(q, '=');
- if (!equals) break; /* bad syntax -- forget it! */
- *equals++ = 0; /* Split at equals */
- next = next_field(equals);
- if (sscanf(equals, "%f", &value)) {
- char * attrib = HTStrip(q);
- if (!strcasecmp(attrib, "q")) quality = value;
- else if (!strcasecmp(attrib, "mxb")) maxbytes = value;
- else if (!strcasecmp(attrib, "mxs")) maxsecs = value;
- }
- q = next;
- } /* scan attributes */
-
- if (field == ACCEPT) {
- if(TRACE) fprintf(stderr, "Daemon: Client accepts %s\n", p);
- #ifndef NOCONVERT
- HTSetConversion(p, "www/present", HTMIMEWrapper,
- quality, 0.0, 0.0); /* @@@@@@@@@@@@ fix zeroes */
- #endif
- }
- } /* if valid line */
- free(line);
- } /* scan lines */
- free(line);
- } /* if protocol */
- } /* if arg */
-
-
- /* Handle command
- */
- if (0==strcmp("GET", line1)) { /* Get a document */
-
- #ifdef OLD_CODE
- if (arg) StrAllocCopy(original, arg)
- keywords=strchr(arg, '?');
- if (keywords) {
- *keywords++ = 0; /* Chop keywords off */
- if (!*keywords) keywords = NULL;
- else {
- StrAllocCopy(key, keywords);
- char *p;
- for (p=key; *p; p++) if (*p == '+') *p = ' ';
- /* Plusses to spaces */
- HTUnEscape(keywords);
- }
- }
- #endif
- status = HTRetrieve(arg, soc);
- free(line1);
- return 0; /* EOF -- please close socket */
- }
-
- HTWriteASCII(soc, "599 Unrecognised method name: `");
- HTWriteASCII(soc, line1);
- HTWriteASCII(soc, "'.\r\n");
-
- if (TRACE) fprintf(stderr,
- "HTDaemon: Unrecognised method `%s'\n", command);
- free(line1);
- return 0; /* End of file - please close socket */
-
-
- } /* handle */
-
-
- /* Handle incoming messages server_loop()
- ** -------------------------
- **
- ** On entry:
- **
- ** timeout -1 for infinite, 0 for poll, else in units of 10ms
- **
- ** On exit,
- ** returns The status of the operation, <0 if failed.
- ** 0 means end of file
- **
- */
- #ifdef __STDC__
- PRIVATE int server_loop(void)
- #else
- PRIVATE int server_loop()
- #endif
- {
- int tcp_status; /* <0 if error, in general */
- int timeout = -1; /* No timeout required but code exists */
- for(;;) {
-
- /* If it's a master socket, then find a slave:
- */
- if (role == master) {
- #ifdef SELECT
- fd_set read_chans;
- fd_set write_chans;
- fd_set except_chans;
- int nfound; /* Number of ready channels */
- struct timeval max_wait; /* timeout in form for select() */
-
- FD_ZERO(&write_chans); /* Clear the write mask */
- FD_ZERO(&except_chans); /* Clear the exception mask */
-
- /* If timeout is required, the timeout structure is set up. Otherwise
- ** (timeout<0) a zero is passed instead of a pointer to the struct timeval.
- */
- if (timeout>=0) {
- max_wait.tv_sec = timeout/100;
- max_wait.tv_usec = (timeout%100)*10000;
- }
-
- for (com_soc=(-1); com_soc<0;) { /* Loop while connections keep coming */
-
-
- /* The read mask expresses interest in the master channel for incoming
- ** connections) or any slave channel (for incoming messages).
- */
-
- /* Wait for incoming connection or message
- */
- read_chans = open_sockets; /* Read on all active channels */
- if (TRACE) printf(
- "Daemon: Waiting for connection or message. (Mask=%x hex, max=%x hex).\n",
- *(unsigned int *)(&read_chans),
- (unsigned int)num_sockets);
- nfound=select(num_sockets, &read_chans,
- &write_chans, &except_chans,
- timeout >= 0 ? &max_wait : 0);
-
- if (nfound<0) return HTInetStatus("select()");
- if (nfound==0) return 0; /* Timeout */
-
- /* We give priority to existing connected customers. When there are
- ** no outstanding commands from them, we look for new customers.
- */
- /* If a message has arrived on one of the channels, take that channel:
- */
- {
- int i;
- for(i=0; i<num_sockets; i++)
- if (i != master_soc)
- if (FD_ISSET(i, &read_chans)) {
- if (TRACE) printf(
- "Message waiting on socket %d\n", i);
- com_soc = i; /* Got one! */
- break;
- }
- if (com_soc>=0) break; /* Found input socket */
-
- } /* block */
-
- /* If an incoming connection has arrived, accept the new socket:
- */
- if (FD_ISSET(master_soc, &read_chans)) {
- soc_addrlen = sizeof(soc_address); /* JS 930107 */
- CTRACE(tfp, "Daemon: New incoming connection:\n");
- tcp_status = accept(master_soc,
- (struct sockaddr *)&soc_address,
- &soc_addrlen);
- if (tcp_status<0)
- return HTInetStatus("accept");
- CTRACE(tfp, "Daemon: Accepted new socket %d\n",
- tcp_status);
- FD_SET(tcp_status, &open_sockets);
- if ((tcp_status+1) > num_sockets)
- num_sockets=tcp_status+1;
-
- } /* end if new connection */
-
-
- } /* loop on event */
-
- #else /* SELECT not supported */
-
- /* if (com_soc<0) No slaves: must accept */
- SockA peer_soc;
- int peer_len = sizeof (peer_soc);
- CTRACE(tfp,
- "Daemon: Waiting for incoming connection...\n");
- #ifdef DECNET
- tcp_status = accept(master_soc, &peer_soc, &peer_len);
- #else /* For which machine is this ??? rsoc is undeclared, what's mdp ? */
- tcp_status = accept(master_soc,
- &rsoc->mdp.soc_tcp.soc_address,
- &rsoc->mdp.soc_tcp.soc_addrlen);
- #endif
- if (tcp_status<0)
- return HTInetStatus("accept");
- com_soc = tcp_status; /* socket number */
- CTRACE(tfp, "Daemon: Accepted socket %d\n", tcp_status);
- /* } end if no slaves */
-
- #endif
-
- } /* end if master */
-
-
- /* com_soc is now valid for read */
-
- {
- SockA addr;
- int namelen = sizeof(addr);
- char ip_address[16];
- #ifdef DECNET
- StrAllocCopy(HTClientHost, "DecnetClient");
- /* TBD */
- #else
- getpeername(com_soc, (struct sockaddr*)&addr, &namelen);
-
- strncpy(ip_address,
- inet_ntoa(addr.sin_addr), sizeof(ip_address));
- StrAllocCopy(HTClientHost, ip_address);
- #endif
- }
-
- /* Read the message now on whatever channel there is:
- */
- CTRACE(tfp,"Daemon: Reading socket %d from host %s\n",
- com_soc, HTClientHost);
-
- tcp_status=HTHandle(com_soc);
-
- if(tcp_status<=0) { /* EOF or error */
- if (tcp_status<0) { /* error */
- CTRACE(tfp,
- "Daemon: Error %d handling incoming message (errno=%d).\n",
- tcp_status, errno);
- /* DONT return HTInetStatus("netread"); error */
- } else {
- CTRACE(tfp, "Daemon: Socket %d disconnected by peer\n",
- com_soc);
- }
- if (role==master) {
- NETCLOSE(com_soc);
- #ifdef SELECT
- FD_CLR(com_soc, &open_sockets);
- #endif
- } else { /* Not multiclient mode */
- #ifdef VM
- return -69;
- #else
- return -ECONNRESET;
- #endif
- }
- } else {/* end if handler left socket open */
- NETCLOSE(com_soc);
- #ifdef SELECT
- FD_CLR(com_soc, &open_sockets);
- #endif
- }
- }; /* for loop */
- /*NOTREACHED*/
- } /* end server_loop */
-
-
- /* Main program
- ** ------------
- **
- ** Options:
- ** -v verify: turn trace output on to stdout
- ** -a addr Use different tcp port number and style
- ** -p port Prefered
- ** -l file Log requests in ths file
- ** -r file Take rules from this file
- ** -R file Clear rules and take rules from file.
- **
- ** Parameters:
- ** directory directory to export
- */
- int main ARGS2 (
- int, argc,
- char**, argv)
- {
- int status;
- #ifdef RULES
- int rulefiles = 0; /* Count number loaded */
- #endif
- char * addr = ""; /* default address */
- char *directory = NULL;
-
- WWW_TraceFlag = 0; /* diagnostics off by default */
-
- if (TRACE) fprintf(stderr,
- "HTDaemon: This is %s, version %s, using libwww version %s\n",
- HTAppName, HTAppVersion, HTLibraryVersion);
-
- #ifdef RULES
- HTClearRules();
- #endif
-
- {
- int a;
-
- for (a=1; a<argc; a++) {
-
- if (0==strcmp(argv[a], "-v")) {
- WWW_TraceFlag = 1;
-
- } else if (0==strcmp(argv[a], "-version")) {
- printf("CERN World-Wide Web Daemon %s, libwww %s\n",
- VD, HTLibraryVersion);
- exit(0);
-
- } else if (0==strcmp(argv[a], "-a")) {
- if (++a<argc) addr = argv[a];
-
- } else if (0==strcmp(argv[a], "-p")) {
- if (++a<argc) {
- addr = (char*)malloc(strlen(argv[a])+10);
- sprintf(addr, "*:%s", argv[a]);
- }
- #ifdef RULES
- } else if (0==strcmp(argv[a], "-r")) {
- if (++a<argc) {
- if (HTLoadRules(argv[a]) < 0) exit(-1);
- rulefiles++;
- }
- } else if (0==strcmp(argv[a], "-R")) {
- rulefiles++; /* Inhibit rule file load */
- #endif
- #ifdef DIR_OPTIONS
- } else if (0==strncmp(argv[a], "-d", 2)) {
- char *p = argv[a]+2;
- for(;*p;p++) {
- switch (argv[a][2]) {
- case 'b': HTDirReadme = HT_DIR_README_BOTTOM; break;
- case 'n': HTDirAccess = HT_DIR_FORBID; break;
- case 'r': HTDirReadme = HT_DIR_README_NONE; break;
- case 's': HTDirAccess = HT_DIR_SELECTIVE; break;
- case 't': HTDirReadme = HT_DIR_README_TOP; break;
- case 'y': HTDirAccess = HT_DIR_OK; break;
- default:
- fprintf(stderr,
- "HTDaemon: bad -d option %s\n", argv[a]);
- exit(-4);
- }
- } /* loop over characters */
- #endif
-
- } else if (0==strcmp(argv[a], "-l")) { /* template */
- if (++a<argc) {
- time(&theTime);
- gmt = gmtime(&theTime);
- log_file_name = malloc(strlen(argv[a]) + 5 + 1);
- sprintf(log_file_name,
- argv[a], (gmt->tm_year) %100 , gmt->tm_mon + 1);
- serverlog = fopen(log_file_name, "a");
- }
- if (!serverlog) {
- fprintf(stderr,
- "Can't open log file %s\n", argv[a]);
- serverlog = stderr;
- }
- } else if (argv[a][0] != '-') { /* Parameter */
- if (!directory) directory = argv[a];
-
- } /*ifs */
- } /* for each arg */
- } /* scope of a */
-
- #ifdef RULES
-
- if (rulefiles==0) { /* No mention */
- if (!directory) {
- if (HTLoadRules(RULE_FILE) < 0) { /* Default rule file? */
- directory = DEFAULT_EXPORT;
- };
- }
- if (directory) {
- char * mapto = malloc(strlen(directory)+5);
- sprintf(mapto, "file://%s%s/*", HTHostName(), directory);
- HTAddRule(HT_Pass, "/*", mapto);
- HTAddRule(HT_Fail, "*", NULL);
- }
- } else {
- if (directory) {
- fprintf(stderr,
- "Warning: -r or -R specified so %s directory param ignored\n",
- directory);
- }
- }
- #endif
-
-
- status = do_bind(addr);
- if (status<0) {
- fprintf(stderr, "Daemon: Bad setup: Can't bind and listen on port.\n");
- fprintf(stderr, " (Possibly server already running, for example).\n");
- exit(status);
- }
-
- status = server_loop();
-
- if (status<0) {
- /* printf("Error in server loop.\n"); not error if inetd-started*/
- exit(status);
- }
-
- exit(0);
- return 0; /* NOTREACHED -- For gcc */
- }
-
-